package cn.tedu.luban.order.main.service;

import cn.tedu.luban.attach.api.AttachApi;
import cn.tedu.luban.attach.protocol.dto.AttachDTO;
import cn.tedu.luban.attach.protocol.param.AttachUpdateParam;
import cn.tedu.luban.attach.protocol.query.AttachQuery;
import cn.tedu.luban.demand.api.DemandApi;
import cn.tedu.luban.order.common.enums.OrderResultEnum;
import cn.tedu.luban.order.common.enums.OrderStatusEnum;
import cn.tedu.luban.order.main.converter.OrderLogConverter;
import cn.tedu.luban.order.main.repository.OrderLogRepository;
import cn.tedu.luban.order.main.repository.OrderRepository;
import cn.tedu.luban.order.protocol.bo.AttachInfoBO;
import cn.tedu.luban.order.protocol.bo.OrderBO;
import cn.tedu.luban.order.protocol.bo.OrderLogBO;
import cn.tedu.luban.order.protocol.dto.OrderMessageDTO;
import cn.tedu.luban.order.protocol.param.*;
import cn.tedu.luban.order.protocol.query.FrontOrderListQuery;
import cn.tedu.luban.passport.protocol.jwt.UserPrinciple;
import cn.tedu.luban.passport.sdk.security.LubanSecurityContext;
import cn.tedu.luban.worker.common.enums.WorkerResultEnum;
import com.tedu.inn.protocol.ListRecordTotalBO;
import com.tedu.inn.protocol.exception.BusinessException;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Service
@Slf4j
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private OrderLogRepository orderLogRepository;
    @Autowired
    private OrderLogConverter orderLogConverter;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    private static final Map<Integer,List<Integer>> STATUS_REGISTRY = new HashMap<>();
    //map注册器
    static{
        // 10 可以转换的目标 【-30，20】
        List<Integer> targets01 = new ArrayList<>();
        targets01.add(20);
        targets01.add(-30);
        STATUS_REGISTRY.put(10,targets01);

        // 20 可以转换的目标 【50】
        List<Integer> targets02 = new ArrayList<>();
        targets02.add(50);
        STATUS_REGISTRY.put(20,targets02);

        // 50 可以转换的目标 【50,30】
        List<Integer> targets03 = new ArrayList<>();
        targets03.add(50);
        targets03.add(30);
        STATUS_REGISTRY.put(50,targets03);

        // 30 可以转换的目标 【40】
        List<Integer> targets04 = new ArrayList<>();
        targets04.add(40);
        STATUS_REGISTRY.put(30,targets04);

    }

    @Transactional(rollbackFor=Exception.class)
    public String createOrder(OrderCreateParam param) throws BusinessException {
        // 1.远程调用demand - 抢单
        grabRequestOrder(param);

        // 2.生成订单 - param缺少必要参数
        // orderNo 订单编号(生成计算规则) - 唯一性（业务性）
        putOrderNoAndStatus(param);
        orderRepository.saveOrder(param);

        // 3. 订单日志的记录
        OrderLogParam orderLogParam = orderLogConverter.assembleOrderLogParam(
                param.getUserId(),
                param.getOrderNo(),
                OrderStatusEnum.ORDER_CREATE_SUCCESS);
        orderLogRepository.saveOrderLog(orderLogParam);

        // 4.超时关闭订单
        // 封装消息对象(携带orderNo)
        Message<String> message = MessageBuilder.withPayload(param.getOrderNo()).build();
        // 发送延迟消息（同步）
        SendResult result = rocketMQTemplate.syncSend("order_delay_topic", message, 500, 4);
        if(!"SEND_OK".equals(result.getSendStatus().toString())){
            throw new BusinessException("6001","生单失败，延迟消息发送失败");
        }

        return param.getOrderNo();
    }


    @Autowired
    private DemandApi demandApi;

    private void grabRequestOrder(OrderCreateParam param) throws BusinessException {
        String requestOrderNo = param.getRequestOrderNo();
        Boolean ok = demandApi.grabOrder(requestOrderNo);
        if(!ok){ //抢单失败
            throw new BusinessException(OrderResultEnum.ORDER_GRAB_FAIL);
        }
    }

    private void putOrderNoAndStatus(OrderCreateParam param) throws BusinessException {
        // orderNo 订单编号(生成计算规则) - 唯一性（业务性）
        // orderNo: BJS2024081500000100952
        // BJS城市编码、20240815日期、000001师傅编号、009施工类型 52工单编号
        String orderNo = UUID.randomUUID().toString();
        param.setOrderNo(orderNo);

        // 登录的userId(controller已补充)
        // Long userId = getUserId();
        // param.setUserId(userId);

        // 订单状态: 10-抢单成功 20-签到 50-施工 30-待结算 40 -完成
        // -10用户关闭 -20商家关闭 -30超时关闭
        param.setStatus(OrderStatusEnum.ORDER_CREATE_SUCCESS.getStatus());
    }

    /**
     * 获取登录的用户userId
     * @return userId
     */
    private Long getUserId() throws BusinessException {
        UserPrinciple user = LubanSecurityContext.getLoginToken();
        if(user == null){
            throw new BusinessException(WorkerResultEnum.USER_TOKEN_VERIFY_FAILED);
        }
        return user.getId();
    }

    public ListRecordTotalBO<OrderBO> orderList(FrontOrderListQuery query) throws BusinessException{
        // 补充userId
        Long userId = getUserId();
        query.setUserId(userId);

        Long total = orderRepository.countOrders(query);

        ListRecordTotalBO<OrderBO> listBO = null;
        if(total==0){
            listBO = new ListRecordTotalBO<>(null,total);
        }else{
            List<OrderBO> orderBOS = orderRepository.queryOrders(query);
            listBO = new ListRecordTotalBO<>(orderBOS,total);
        }
        return listBO;
    }


    public OrderBO orderInfo(String orderNo) throws BusinessException {
        // 检查订单是否存在，存在返回数据，不存在抛出异常
        OrderBO orderBO = checkEmpty(orderNo);

        // 封装订单signTime(签到时间)，封装完成时间 - 来源order_log表
        List<OrderLogBO> orderLogs = orderLogRepository.getOrderLogByOrderNo(orderNo);
        if(CollectionUtils.isEmpty(orderLogs)){
            throw new BusinessException("6002","订单日志数据不存在");
        }

        // 封装订单signTime(签到时间)，封装完成时间
        Long signTime = null;
        Long finishTime = null;
        for(OrderLogBO orderLog : orderLogs){
            String operateName = orderLog.getOperateName();
            // if("签到".equals(operateName)){
            // 生成的数据用的就是订单状态的枚举，所以在此位置也可以用于判断
            if(OrderStatusEnum.ORDER_SIGN_SUCCESS.getMessage().equals(operateName)){
                signTime = orderLog.getOperateTime();
            }
            if(OrderStatusEnum.ORDER_FINISH.getMessage().equals(operateName)){
                finishTime = orderLog.getOperateTime();
            }
        }
        orderBO.setSignTime(signTime);
        orderBO.setFinishTime(finishTime);

        // RPC 封装图片信息 40 50状态的订单才有图片信息
//        if(orderBO.getStatus() == 40 || orderBO.getStatus() == 50){
//            putAttachUrls(orderBO);
//        }
        putAttachUrls(orderBO);

        return orderBO;
    }

    @Autowired
    private AttachApi attachApi;
    /**
     * RPC 封装图片信息
     */
    private void putAttachUrls(OrderBO orderBO) {
        // 查询当前订单所绑定的图片
        AttachQuery query = new AttachQuery();
        // 业务类型：200-订单、 100-师傅
        query.setBusinessType(200);
        query.setBusinessId(orderBO.getId().intValue());
        //rpc调用
        List<AttachDTO> attachDTOs = attachApi.getAttachInfoByParam(query);
        List<AttachInfoBO> bos = new ArrayList<>();

        for(AttachDTO attachDTO : attachDTOs){
            // url = 环境url前缀 + dto的文件uuid（文件名）
            String url = "http://localhost:8092/static/"+attachDTO.getFileUuid();
            AttachInfoBO bo = new AttachInfoBO();
            bo.setUrl(url);
            bos.add(bo);
        }
        orderBO.setAttachInfoBO(bos);
    }

    /**
     * 检查订单是否存在，存在返回数据，不存在抛出异常。【逻辑可以复用】
     */
    private OrderBO checkEmpty(String orderNo) throws BusinessException {
        OrderBO orderBO = orderRepository.getOrderByOrderNo(orderNo);
        if(orderBO.getId() == null){
            throw new BusinessException(OrderResultEnum.ORDER_EMPTY);
        }
        return orderBO;
    }

    public void orderSign(OrderSignParam param) throws BusinessException {
        // 1.检查订单是否存在，存在返回数据，不存在抛出异常
        String orderNo = param.getOrderNo();
        OrderBO orderBO = checkEmpty(orderNo);

        // 2.检查订单是否可以签到(不做)
        checkLocations();

        // 3.流转判断: 签到成功，订单状态变更为20-签到
        Integer currentStatus = orderBO.getStatus();
        Integer targetStatus = OrderStatusEnum.ORDER_SIGN_SUCCESS.getStatus();
        checkStatus(currentStatus,targetStatus);

        // 4.修改订单状态
        OrderParam orderParam = new OrderParam();
        orderParam.setOrderNo(orderNo);
        orderParam.setStatus(targetStatus);
        orderRepository.updateStatus(orderParam);

        // 5.订单日志的记录
        Long userId = getUserId();
        OrderLogParam orderLogParam = orderLogConverter.assembleOrderLogParam(
                userId,
                orderNo,
                OrderStatusEnum.ORDER_SIGN_SUCCESS);
        orderLogRepository.saveOrderLog(orderLogParam);

    }

    private void checkStatus(Integer currentStatus, Integer targetStatus) throws BusinessException {

        /*
        if(currentStatus==10 && (targetStatus!=20 || targetStatus!=-30)){
            // 流转失败
        }else if(currentStatus==20 && targetStatus!=50){
            // 流转失败
        }else if (){

        }
        */
        // 设计模式(23种)： 策略模式
        /*
        boolean b = STATUS_REGISTRY.containsKey(currentStatus);
        if(!b){
            throw new BusinessException("6001","订单状态流转失败");
        }
        List<Integer> destStatus = STATUS_REGISTRY.get(currentStatus);
        if(!destStatus.contains(targetStatus)){
            throw new BusinessException("6001","订单状态流转失败");
        }
        */
        // STATUS_REGISTRY.get(currentStatus).contains(targetStatus) 链式调用
       if(STATUS_REGISTRY.containsKey(currentStatus) && STATUS_REGISTRY.get(currentStatus).contains(targetStatus)){
           return;
       }
       throw new BusinessException("6001","订单状态流转失败");

    }

    private void checkLocations() {
    }

    public void orderConfirm(OrderConfirmParam param) throws BusinessException{
        // 1.检查参数attachIds值是否为空，为空则抛出异常
        checkEmptyPictures(param);
        // 2.检查订单是否存在，存在返回数据，不存在抛出异常
        OrderBO orderBO = checkEmpty(param.getOrderNo());
        // 3.检查流转状态
        Integer currentStatus = orderBO.getStatus();
        Integer targetStatus = OrderStatusEnum.ORDER_ATTACH_CONFIRM.getStatus();
        checkStatus(currentStatus,targetStatus);

        // 4.判断订单状态 !=50的时候肯定就是20 20->50调用repository  50->50无需调用repository
        if(currentStatus != OrderStatusEnum.ORDER_ATTACH_CONFIRM.getStatus()){
            //修改订单状态
            // update order set status = 50 where order_no = param.getOrderNo() ;
            OrderParam orderParam = new OrderParam();
            orderParam.setOrderNo(param.getOrderNo());
            orderParam.setStatus(targetStatus);
            orderRepository.updateStatus(orderParam);
        }

        // 5.订单日志的记录
        OrderLogParam orderLogParam = orderLogConverter.assembleOrderLogParam(
                getUserId(),
                param.getOrderNo(),
                OrderStatusEnum.ORDER_ATTACH_CONFIRM);
        orderLogRepository.saveOrderLog(orderLogParam);

        // 6.RPC调用，绑定图片信息
        bindPictures(param,200,orderBO.getId().intValue());
    }

    private void bindPictures(OrderConfirmParam param, int businessType, int businessId) throws BusinessException {
        // 图片ids
        List<Long> attachIds = param.getAttachIds();
        // 封装图片信息
        List<AttachUpdateParam> attachUpdateParams = new ArrayList<>();

        for (Long attachId : attachIds){
            AttachUpdateParam attachUpdateParam = new AttachUpdateParam();
            attachUpdateParam.setId(attachId.intValue());
            attachUpdateParam.setBusinessType(businessType);
            attachUpdateParam.setBusinessId(businessId);
            attachUpdateParam.setIsCover(1); // 是否封面页 （随意给值，不然报错）
            attachUpdateParams.add(attachUpdateParam);
        }

        int result = attachApi.batchUpdateAttachByIdList(attachUpdateParams);
        if(result == 0){
            throw new BusinessException(OrderResultEnum.ATTACH_CONFIRM_ERROR);
        }
    }

    private void checkEmptyPictures(OrderConfirmParam param) throws BusinessException {
        List<Long> attachIds = param.getAttachIds();
        if(CollectionUtils.isEmpty(attachIds)){
            throw new BusinessException(OrderResultEnum.ATTACH_CONFIRM_ERROR);
        }
    }


    public void orderDelay(String orderNo) throws BusinessException {
        // 1.检查订单是否存在，存在返回数据，不存在抛出异常
        OrderBO orderBO = checkEmpty(orderNo);
        // 2.拿到status，判断 ==10
        Integer currentStatus = orderBO.getStatus();
        // 3.1.不等于10 不是超时，什么都不做（结束调用）
        if(currentStatus != OrderStatusEnum.ORDER_CREATE_SUCCESS.getStatus()){
            // 订单已经被签到
            return;
        }
        // 3.2.是超时，修改订单状态为-30
        Integer targetStatus = OrderStatusEnum.CANCELLED_BY_EXPIRE.getStatus();
        // 流转判断
        checkStatus(currentStatus,targetStatus);
        // 修改订单状态
        OrderParam orderParam = new OrderParam();
        orderParam.setOrderNo(orderNo);
        orderParam.setStatus(targetStatus);
        orderRepository.updateStatus(orderParam);
        // 4.订单日志的记录(关闭)  999L:代表系统关闭订单
        OrderLogParam orderLogParam = orderLogConverter.assembleOrderLogParam(
                999L,orderNo,OrderStatusEnum.CANCELLED_BY_EXPIRE);
        orderLogRepository.saveOrderLog(orderLogParam);
        // 5.rpc调用，调用归还需求单
        Boolean returned = demandApi.returnOrder(orderBO.getRequestOrderNo());
        if(!returned){
            throw new BusinessException("9002","归还需求单失败");
        }

    }

    public void orderFinish(OrderFinishParam param) throws BusinessException {
        // 1.检查订单是否存在，存在返回数据，不存在抛出异常
        OrderBO orderBO = checkEmpty(param.getOrderNo());
        // 2.检查流转状态 50 -> 30
        Integer currentStatus = orderBO.getStatus();
        Integer targetStatus = OrderStatusEnum.ORDER_SERVICE_SUCCESS_UN_SETTLE.getStatus();
        checkStatus(currentStatus,targetStatus);
        // 3.订单状态为30
        OrderParam orderParam = new OrderParam();
        orderParam.setOrderNo(param.getOrderNo());
        orderParam.setStatus(targetStatus);
        orderRepository.updateStatus(orderParam);

        // 4.记录订单日志
        OrderLogParam orderLogParam = orderLogConverter.assembleOrderLogParam(
                getUserId(),
                param.getOrderNo(),
                OrderStatusEnum.ORDER_SERVICE_SUCCESS_UN_SETTLE);
        orderLogRepository.saveOrderLog(orderLogParam);

        // 5.发送消息
        OrderMessageDTO dto = new OrderMessageDTO();
        BeanUtils.copyProperties(orderBO,dto);
        // 将 dto封装到 message 对象中
        Message<OrderMessageDTO> message = MessageBuilder.withPayload(dto).build();
        // 发送消息异步
//        .asyncSend("order-finish-topic",message,new SendCallback() {
//            // 异步线程接收rocketMQ返回发送成功 调用
//            @Override
//            public void onSuccess(SendResult sendResult) {
//                log.info("发送成功：{}",sendResult);
//            }
//            // 异步线程接收rocketMQ返回发送失败 调用
//            @Override
//            public void onException(Throwable throwable) {
//                log.error("发送失败：{}",throwable);
//            }
//        });
        // 同步
        SendResult sendResult = rocketMQTemplate.syncSend("order-finish-topic", message);
        if(!"SEND_OK".equals(sendResult.getSendStatus().toString())){
            throw new BusinessException("6001","订单完成失败，消息发送失败");
        }
    }

    public void orderComplete(String orderNo) throws BusinessException {
        // 1.检查订单是否存在，存在返回数据，不存在抛出异常
        OrderBO orderBO = checkEmpty(orderNo);
        // 2.检查流转状态 30 -> 40
        Integer currentStatus = orderBO.getStatus();
        Integer targetStatus = OrderStatusEnum.ORDER_FINISH.getStatus();
        checkStatus(currentStatus,targetStatus);
        // 3.更新订单状态
        OrderParam orderParam = new OrderParam();
        orderParam.setOrderNo(orderNo);
        orderParam.setStatus(targetStatus);
        orderRepository.updateStatus(orderParam);
        // 4.订单日志的记录
        OrderLogParam orderLogParam = orderLogConverter.assembleOrderLogParam(
                getUserId(),
                orderNo,
                OrderStatusEnum.ORDER_FINISH);
        orderLogRepository.saveOrderLog(orderLogParam);


    }
}











